Oracle Database 23ai added 300+ new features like the new VECTOR datatype that get most of the attention, but often overlooked are two new features that dramatically expand support for complex geospatial problem-solving. In this article we’ll take a closer look at vector tiling – a toolset that already has everything your developers need to create ultra-fast maps in standard geospatial applications.
OK, I have to admit it: I’m a map junkie. I’ve built all kinds of mapping applications for the past several years using either Oracle APEX’s Native Map Region (NMR) toolset when I wanted a low-code solution with excellent control over features like legends and layers, and Oracle Spatial Studio when I didn’t have time to write complex SQL to quickly review how spatial data was distributed.
One pain point I’ve run into involves response time, especially when I’m displaying tens or even hundreds of thousands of individual points on a map on different layers. Even my laptop with a reasonably robust GPU and memory cannot cope with those demands. For example, the map in Figure 1 was built in APEX 24.2 using NMR to display over 157,000 separate longitude/latitude points on three different map layers, and it typically takes between 45 – 180 seconds to load completely. My pain is not new or just my pain; loading map content quickly when there are huge volumes of individual points to display has been a challenge for GIS applications since they were first born in the mid-1960s.

Figure 1. Displaying +157K Distinct Geographic Points in Oracle APEX 24.2
But there’s good news for Oracle developers working with geospatial applications today: There’s an intriguing solution in Oracle Database 23ai that significantly reduces map response time. Vector tiles encode all mapping data – both point / polygonal information and corresponding metadata – into a unique compressed binary format so specialized GIS applications like QGIS or MapLink can interpret and display this format at blinding speed.
Since the size of the data for vector tiles is dramatically smaller than previous formats, network response time is also reduced, so ultimately response time for any map activity – scrolling, zooming, or selecting individual points and displaying their metadata – often improves by several orders of magnitude.
So, What Are Vector Tiles?
The easiest way to envision vector tiles is to imagine a Mercator map projection of the surface of the Earth. The resulting projection of that geometry onto a flat surface results in a series of evenly-sized squares that can be grouped into just four at the lowest zoom level (Figure 2).

Figure 2. Vector Tiles: Grouping Ever-Lower Levels of Mercator Projection Squares
Here’s the real brilliance of vector tiles: At the next lowest zoom level, each one of those four squares also contains another set of four squares, which at the next zoom level contains yet another four squares, and so on until we reach a zoom level of 22, or 223 squares. At this lowest level, each vector tile is a square that’s just under one square meter in area.
Vector tiles work really well for mapping applications because at a high zoom level, it’s easy to see where points are concentrated on a map, and as you drill down into the lowest levels of the map, the points are already present so there’s no need to make another round trip to the map server to retrieve them and any related metadata.
Vector Tiles: Preparing For Implementation
Let’s illustrate how easy it is to leverage 23ai’s vector tile capabilities with a straightforward use case: A public utility is researching where it should place long-term battery storage sites close enough to existing wind turbines, solar panel farms, and other renewable energy resources it’s planning to develop. Since EVs aren’t going to disappear and are likely to increase among its customer base, the utility also needs to identify all existing EV charging stations so they can provide them with energy captured at the battery storage sites.
To build out this use case, I’ve created and populated three different tables containing geolocations for EV charging stations, wind turbines, and photovoltaic (PV) solar panel farms throughout the United States. These data were downloaded and curated from the US National Renewal Energy Laboratory (NREL) website, and contains site-specific information including each wind turbine’s latitude and longitude.
Note: in the Appendix at the end of this article, you can find details about how to get the structures and data to test this out yourself.
To keep this example simpler for now, I’ll focus on the EXISTING_WIND_TURBINES
table. The DDL to create that table is shown in Figure 3.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
DROP TABLE IF EXISTS existing_wind_turbines PURGE; CREATE TABLE IF NOT EXISTS existing_wind_turbines ( usgs_case_id NUMBER(10) , faa_digital_obstacle_id VARCHAR2(10) , faa_aeronautical_study_nbr VARCHAR2(20) , usgs_prior_id NUMBER(10) , eia_860_id NUMBER(10) , state_abbr VARCHAR2(02) , county VARCHAR2(60) , fips VARCHAR2(09) , project_name VARCHAR2(60) , project_year NUMBER(04) , project_turbines NUMBER(05) , project_capacity NUMBER(15,03) , manufacturer VARCHAR2(80) , model VARCHAR2(40) , turbine_capacity NUMBER(15,03) , turbine_hub_height NUMBER(15,03) , turbine_rotor_diameter NUMBER(15,03) , turbine_rotor_swept_area NUMBER(15,03) , turbine_total_height NUMBER(15,03) , is_turbine_retrofitted NUMBER(01) , retrofit_yr NUMBER(04) , is_turbine_offshore NUMBER(01) , turbine_attribute_confidence NUMBER(01) , turbine_location_confidence NUMBER(01) , imaged_on DATE , image_source VARCHAR2(20) , longitude NUMBER(15,08) , latitude NUMBER(15,08) , geometry SDO_GEOMETRY ); ALTER TABLE existing_wind_turbines ADD CONSTRAINT existing_wind_turbines_pk PRIMARY KEY (usgs_case_id) USING INDEX ( CREATE UNIQUE INDEX existing_wind_turbines_pk_idx ON existing_wind_turbines (usgs_case_id) ); |
Figure 3. EXISTING_WIND_TURBINES
Table Creation
After uploading the data via the SQL Web Developer data loading facility, I updated the contents of the GEOMETRY
column with the longitude and latitude of every wind turbine site. Finally, I created a spatial index on each table’s GEOMETRY
column (Figure 4), thus enabling the geospatial features of the SDO_GEOMETRY
datatype so that mapping technology could interact with that column’s data.
1 2 3 4 5 6 7 8 9 10 |
UPDATE existing_wind_turbines SET geometry = SDO_GEOMETRY(longitude, latitude); COMMIT; DROP INDEX IF EXISTS existing_wind_turbines_spidx; CREATE INDEX IF NOT EXISTS existing_wind_turbines_spidx ON existing_wind_turbines (geometry) INDEXTYPE IS MDSYS.SPATIAL_INDEX_V2 PARAMETERS ('LAYER_GTYPE=POINT'); |
Figure 4. Updating EXISTING_WIND_TURBINES.GEOMETRY
and Creating Its Spatial Index
Building Vector Tile Output From an SDO_GEOMETRY Column
Returning geospatial data from the EXISTING_WIND_TURBINES
table as a series of vector tiles in binary format is remarkably simple to accomplish via the new 23ai SDO_UTIL.GET_VECTORTILE
function. Note that in this case, I specified the exact zoom level and X and Y coordinates of the tiles I’m interested in retrieving (Figure 5), but we’ll look at parameterizing this function call shortly.
1 2 3 4 5 6 7 8 9 |
SELECT SDO_UTIL.GET_VECTORTILE( TABLE_NAME=>'EXISTING_WIND_TURBINES', GEOM_COL_NAME=>'GEOMETRY', ATT_COL_NAMES=> SDO_STRING_ARRAY('USGS_CASE_ID','COUNTY','STATE_ABBR' ,'TURBINE_TOTAL_HEIGHT','TURBINE_CAPACITY' ,'LONGITUDE','LATITUDE'), TILE_X=>129, TILE_Y=>187, TILE_ZOOM=>9, MAX_FEATURES=>255) AS gobbledygook; |
Figure 5. Generating Limited Vector Tile Output from EXISTING_WIND_TURBINES
So what gets returned from the query in Figure 5? Here’s a snippet of the resulting output (Figure 6); you’ll note it appears as gibberish on first glance. However, a spatial application that consumes this output sees this binary output as tightly-compressed spatial data comprising the vector tiles themselves as well as any specific metadata about each point. The spatial application can then display the vector tiles within a separate mapping layer at extreme speed without the need to make a return trip to the sending server.

Figure 6. GET_VECTORTILE
Function: Sample Output
Generating Vector Tiles Via ORDS REST API
The trick to transforming these vector tiles into something that mapping software can use to display them is leveraging Oracle’s ORDS REST
API toolset. Figure 7 shows how I built an ORDS REST API
module named wind_turbines
, defined a corresponding template that accepts variable values, and finally defined a handler that returns just the required appropriate vector tiles as a BLOB
based on the parameter values specified.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
BEGIN ORDS.DELETE_MODULE(wind_turbines'); END; / BEGIN ORDS.DEFINE_MODULE( P_MODULE_NAME => 'wind_turbines' , P_BASE_PATH => '/existing_turbines/' , P_ITEMS_PER_PAGE => 50 , P_STATUS => 'PUBLISHED' ); COMMIT; END; / BEGIN ORDS.DEFINE_TEMPLATE( P_MODULE_NAME => 'wind_turbines' , P_PATTERN => 'vt/:z/:x/:y' , P_PRIORITY => 0 , P_ETAG_TYPE => 'HASH' ); COMMIT; END; / BEGIN ORDS.DEFINE_HANDLER( P_MODULE_NAME => 'wind_turbines' , P_PATTERN => 'vt/:z/:x/:y' , P_METHOD => 'GET' , P_SOURCE_TYPE => ORDS.SOURCE_TYPE_MEDIA , P_SOURCE => 'SELECT ''application/vnd.mapbox-vector-tile'' AS mediatype ,SDO_UTIL.GET_VECTORTILE( TABLE_NAME => ''EXISTING_WIND_TURBINES'' ,GEOM_COL_NAME => ''GEOMETRY'' ,ATT_COL_NAMES => SDO_STRING_ARRAY(''USGS_CASE_ID'',''FIPS'',''COUNTY'' ,''STATE_ABBR'',''TURBINE_TOTAL_HEIGHT'',''TURBINE_CAPACITY'' ,''LATITUDE'',''LONGITUDE'') ,TILE_X => :x ,TILE_Y_PBF => :y ,TILE_ZOOM => :z) AS vtile FROM DUAL' , P_ITEMS_PER_PAGE => 50 ); COMMIT; END; / |
Figure 7. ORDS REST API
Module, Template, and Handler for EXISTING_WIND_TURBINES
Vector Tiles
The ORDS
module and template code is pretty simple, but the ORDS
handler code is a bit trickier to understand, so let’s take a closer look:
- I’ve set
P_SOURCE_TYPE
toORDS.SOURCE_TYPE_MEDIA
.This tells ORDS to return the result set in binary format that also includes an accompanyingHTTP Content-Type
header. - The code in
P_SOURCE
uses aGET_VECTORTILE
query that will return the latitude, longitude, and other metadata for each wind turbine. - Note the column I’ve labeled
mediatype
is set to a static value ofapplication/vnd.mapbox-vector-tile
. That matches the expected output of the vector tile binary result set.
The query also expects three bind variable values to be supplied during execution:
TILE_ZOOM
determines the actual number of tiles needed to divide up a map into smaller and smaller pieces. As the value ofTILE_ZOOM
increases, so do the number of tiles … and thus finer and finer detail can be retrieved and displayed. If I specifyTILE_ZOOM
as four (4), the map would be divvied up into 24 x 24 tiles, or 256 (16 x 16) tiles; if it’s set to eight (8), the map would be cut up into 65,536 individual tiles.TILE_X
defines the starting X coordinate of the grid of vector tiles to be fetched.- Lastly,
TILE_Y_PBF
defines the Y coordinate within the grid of tiles we want to fetch. The reason we’re using this format of the Y coordinate? It refers to the name of the proto-buffer file (PBF) that will be retrieved and displayed as the base layer of the map when the corresponding mapping points are displayed above that base layer.
Enabling Vector Tile Displays with MapLibre GL
While I could use Oracle Spatial Studio to map vector tiles, it takes some extra time to configure a user account and set up all the metadata and infrastructure to leverage that powerful toolset. Here’s a slightly faster method I can use as a touchstone to verify that I’ve set up my Oracle 23ai database-side code properly for mapping purposes with easy-to-understand HTML:
- I’ll leverage Microsoft Visual Studio’s Live Server browser extension to display them within an HTML frame using the MapLibre GL JS library.
- The first section of the HTML sample code in Figure 8 handles map server parameters for displaying the map itself, including a base map style. Here, I’ve used the Open Street Map (OSM) “Bright” style, but there are several other OSM styles available.
- I’ve also specified a closer initial zoom level (4) for the map and focused the initial center of the map (longitude -90 degrees, latitude 43 degrees) near south-central Wisconsin in the United States.

Figure 8. MapLibre Live Server Demonstration: Initialization & Setup
The rest of the HTML body is where the real magic of vector tiles shines through. In Figure 8, I’ve highlighted the code that builds that handles mapping all of the existing wind turbine points:
- The code in the red box accesses the ORDS endpoint for the existing wind turbines I created in Figure 7. It works in concert with the MapLibreGL interface to shift the display to exactly the contents of the vector tiles at the corresponding zoom level for the corresponding X and Y coordinates.
- The code in the blue box specifies how each wind turbine’s point should be displayed on the map. To differentiate them from other points in other layers, I’ve selected I’m displaying each turbine as a small green circle with a slightly darker green outline.
- And the code in the green box? It grabs the related metadata for the map point that I’ve clicked on, formats it into a character string, and displays it as a pop-up. This gives me the ability to drill into specific wind turbine characteristics – in this case, its unique identifier, the state and FIPS code where it’s located, and the turbine’s height and generation capacity in KW.
-
Figure 9. MapLibre Live Server Demonstration: Adding a Map Layer
The End Result? Blazingly Fast Map Displays.
As much as I love using APEX Native Map Regions, I was thrilled to see that displaying map points with vector tiles and MapLibreGL is at least an order of magnitude faster … and I’m running my tests on a low-cost, underpowered ASUS VivoBook laptop without any display accelerators or GPUs. I was able to traverse, zoom in, zoom out, and navigate within the map frame with significantly less lag time.
And I saw no degradation in performance when I added two more mapping layers – existing photovoltaic arrays and existing EV charging stations – to my mapping configuration. Figure 10 shows the result, with EV chargers shown in blue and PV arrays shown in red within the continental the United States.

Figure10. Vector Tile Maps: Wide-Area Map Display
Figure 11 shows the upper Midwest of the USA at a lower zoom level. Note that individual points are still quite clearly displayed even at this zoom level.

Figure 11. Vector Tile Maps: Drilling Down to Lower Levels of Detail
Finally, Figure 12 shows an example of drilling down deeply into the map and then showing some of the metadata for one of the hundreds of thousands of map points – in this case, a single wind turbine in middle of Minnesota.

Figure 12. Vector Tile Maps: Zooming In Even Lower
Enough Tiling Already! What’s Coming Next?
Vector tiles are just half of the new mapping capabilities that the Oracle 23ai converged database provides for your IT organization’s mapping requirements. They’re relatively simple to implement if you’ve already embraced the SDO_GEOMETRY datatype for geospatial applications, and they’re also likely to rescue your DevOps team from painful experiments with different geospatial databases.
In the next article in this series, we’ll look at how Hierarchical Hexagonal Indexing (H3) features in Oracle 23ai handle another type of mapping problem: visually aggregating the data that’s often implicitly attached to hordes of individual geographic points so we can provide meaningfulness at any zoom level.
Appendix
If you’d like to experiment with the APEX 24.2 app and the MapBox examples presented to see the dramatic difference in performance, you can download all application code samples, DDL to create tables and spatial indexes, and GIS datasets themselves here.
Load comments